package com.ld.admin.aop.timedtask;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.ld.admin.model.timedtask.SysTimedtaskLogModel;
import com.ld.admin.model.timedtask.SysTimedtaskUserInfo;
import com.ld.common.util.task.TaskThread;
import com.ld.common.util.task.TimedTaskLogUtil;
import com.ld.shieldsb.common.web.util.Web;
import com.ld.shieldsb.common.web.util.WebUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 定时任务切面
 * 
 * @ClassName LogAspect
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2018年6月14日 上午11:04:55
 *
 */
@Aspect
@Order(value = 10) // 值越小的 aspect 越先执行,也可实现org.springframework.core.Ordered接口，实现它的getOrder()方法
@Component
@Slf4j
public class TimedtaskAspect {
    /**
     * 1.通配符<br>
     * [*] 匹配任意字符，但只能匹配一个元素<br>
     * [..] 匹配任意字符，可以匹配任意多个元素，表示类时，必须和*联合使用<br>
     * [+] 必须跟在类名后面，如Horseman+，表示类本身和继承或扩展指定类的所有类<br>
     * 切点表达式分为 修饰符 返回类型 包路径 方法名 参数<br>
     * 2.切点表达式<br>
     * 3.逻辑运算符<br>
     * 表达式可由多个切点函数通过逻辑运算组成<br>
     * && 与操作，求交集，也可以写成and<br>
     * 例如 execution(* chop(..)) && target(Horseman) 表示Horseman及其子类的chop方法<br>
     * || 或操作，任一表达式成立即为true，也可以写成 or<br>
     * 例如 execution(* chop(..)) || args(String) 表示名称为chop的方法或者有一个String型参数的方法<br>
     * ! 非操作，表达式为false则结果为true，也可以写成 not<br>
     * 例如 execution(* chop(..)) and !args(String) 表示名称为chop的方法但是不能是只有一个String型参数的方法
     */
    // 表示TaskThread及其子类的run方法
    @Pointcut("execution(* run(..)) && target(com.ld.common.util.task.TaskThread)")
    public void commonAccessLog() {
    }

    // 环绕通知,环绕增强，相当于MethodInterceptor
    @Around("commonAccessLog()")
    public Object around(ProceedingJoinPoint pjp) {
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        SysTimedtaskUserInfo userInfo = null; // 用户信息
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            if (request != null) {
                userInfo = new SysTimedtaskUserInfo(); // 用户信息
                userInfo.setUserId(Web.Util.getSessionUserId(request));
                userInfo.setUserName(Web.Util.getSessionUserName(request));
                userInfo.setIp(WebUtil.getIpAddr(request));
            }
        }
        Object target = pjp.getTarget();
        if (target instanceof TaskThread) {
            TaskThread taskModel = (TaskThread) target;

            // 日志
            TimedTaskLogUtil.saveLogbyDB(SysTimedtaskLogModel.Type.TYPE_BEGIN, taskModel.getTaskModel().getId(), "任务开始", userInfo);
            try {
                Object o = pjp.proceed();
                // 日志
                TimedTaskLogUtil.saveLogbyDB(SysTimedtaskLogModel.Type.TYPE_END, taskModel.getTaskModel().getId(), "任务结束", userInfo);
                return o;
            } catch (Throwable e) {
                log.error("", e);
            }
        }
        return null;

    }

    // 后置异常通知
    @AfterThrowing(throwing = "ex", pointcut = "commonAccessLog()")
    public void throwss(JoinPoint joinPoint, Throwable ex) {
        String msg = "异常：" + ex.toString();
        String exceptionInfo = "";
        StackTraceElement[] throwable = ex.getStackTrace();
        for (StackTraceElement ste : throwable) {
            exceptionInfo += "\n " + ste;
        }
        log.warn(exceptionInfo);

        Object target = joinPoint.getTarget();
        if (target instanceof TaskThread) {
            TaskThread taskModel = (TaskThread) target;

            // 日志
            TimedTaskLogUtil.saveLogbyDB(SysTimedtaskLogModel.Type.TYPE_RUN_EXCEP, taskModel.getTaskModel().getId(), msg, null);
        }

    }
}